package com.eight.lesson6;

import java.util.Comparator;
import java.util.HashSet;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.eight.lesson4.Dish;
import com.eight.lesson4.DishList;

/**
 * 归约和汇总
 * @author Administrator
 *
 */
public class DishTest {

	public static void main(String[] args) {
		List<Dish> menu = DishList.getDishs();
		System.out.println("数一数菜单里有多少种菜：");
		long howManyDishes1 = menu.stream().collect(Collectors.counting());
		System.out.println("结果1："+howManyDishes1);
		long howManyDishes2 = menu.stream().count();
		System.out.println("结果1："+howManyDishes2);
		
		System.out.println();
		
		System.out.println("查找流中的最大值和最小值");
		System.out.println("找出菜单中热量最高的菜：");
		Comparator<Dish> dishCaloriesComparator = Comparator.comparing(Dish::getCalories);
		Optional<Dish> mostCaloriesDish = menu.stream().collect(Collectors.maxBy(dishCaloriesComparator));
		if(mostCaloriesDish.isPresent()) {
			System.out.println("菜名："+mostCaloriesDish.get().getName()+",热量："+mostCaloriesDish.get().getCalories());
		}else {
			System.out.println("无结果");
		}
		
		System.out.println();
		
		System.out.println("菜单列表总热量：");
		long totalCalories = menu.stream().collect(Collectors.summingLong(Dish::getCalories));
		System.out.println("结果："+totalCalories);
		
		System.out.println("菜单列表平均热量：");
		double avgCalories = menu.stream().collect(Collectors.averagingDouble(Dish::getCalories));
		System.out.println("结果："+avgCalories);
		
		System.out.println();
		
		System.out.println("菜单中元素个数，菜肴热量总和、平均值、最大值、最小值：");
		IntSummaryStatistics intSummaryStatistics = menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
		System.out.println("结果："+intSummaryStatistics);
		
		System.out.println();
		
		System.out.println("把菜单中所有菜的名称连接起来：");
		String shortMenu = menu.stream().map(Dish::getName).collect(Collectors.joining(", "));
		System.out.println("结果："+shortMenu);
		
		System.out.println();
		
		System.out.println("广义的归约汇总");
		System.out.println("菜单列表总热量：");
		long sunCalories = menu.stream().collect(Collectors.reducing(0, Dish::getCalories, (i,j) -> i+j));
		System.out.println("结果："+sunCalories);
		System.out.println("菜单列表热量最高：");
		Optional<Dish> maxCaloriesDish = menu.stream()
				.collect(Collectors.reducing((d1,d2) -> d1.getCalories()>d2.getCalories()?d1:d2));
		if(maxCaloriesDish.isPresent()) {
			System.out.println("菜名："+maxCaloriesDish.get().getName()+",热量："+maxCaloriesDish.get().getCalories());
		}else {
			System.out.println("无结果");
		}
		
		System.out.println();
		
		System.out.println("分组");
		System.out.println("按菜肴类型分组：");
		Map<Dish.Type, List<Dish>> dishesByType = menu.stream()
				.collect(Collectors.groupingBy(Dish::getType));
		System.out.println("结果："+dishesByType);
		
		System.out.println("根据热量高低分组：");
		Map<CaloricLevel, List<Dish>> dishesByCalories = menu.stream()
				.collect(Collectors.groupingBy(dish -> {
					if(dish.getCalories()<300) return CaloricLevel.DIET;
					else if(dish.getCalories()<700) return CaloricLevel.NORMAL;
					else return CaloricLevel.FAT;
				}));
		System.out.println("结果："+dishesByCalories);
		
		System.out.println();
		
		System.out.println("多级分组");
		System.out.println("根据菜肴类型和热量高低分组：");
		Map<Dish.Type, Map<CaloricLevel, List<Dish>>> dishesByTypeCalories = 
				menu.stream().collect(
						Collectors.groupingBy(Dish::getType, 
								Collectors.groupingBy(dish -> {
									if(dish.getCalories()<300) return CaloricLevel.DIET;
									else if(dish.getCalories()<700) return CaloricLevel.NORMAL;
									else return CaloricLevel.FAT;
								})));
		System.out.println("结果："+dishesByTypeCalories);
		
		System.out.println("每类菜有多少个：");
		Map<Dish.Type, Long> typesCount = menu.stream().collect(
				Collectors.groupingBy(Dish::getType, Collectors.counting()));
		System.out.println("结果："+typesCount);
		
		System.out.println("每种类型最高热量的菜：");
		Map<Dish.Type, Optional<Dish>> mostTypeByCalories = menu.stream().collect(
				Collectors.groupingBy(Dish::getType, 
						Collectors.maxBy(
								Comparator.comparing(Dish::getCalories))));
		System.out.println("结果："+mostTypeByCalories);
		
		System.out.println("查找每个子组中热量最高的菜：");
		Map<Dish.Type, Dish> mostCaloriesByType = menu.stream().collect(
				Collectors.groupingBy(Dish::getType, 
						Collectors.collectingAndThen(
								Collectors.maxBy(
										Comparator.comparing(Dish::getCalories)), 
								Optional::get)));
		System.out.println("结果："+mostCaloriesByType);
		
		System.out.println("求每组菜热量总和：");
		Map<Dish.Type, Integer> sumCaloriesByType = menu.stream().collect(
				Collectors.groupingBy(Dish::getType, 
						Collectors.summingInt(Dish::getCalories)
						));
		System.out.println("结果："+sumCaloriesByType);
		
		System.out.println("每种类型的Dish，菜单中都有哪些CaloricLevel：");
		Map<Dish.Type, Set<CaloricLevel>> caloricLevelByType = 
				menu.stream().collect(
						Collectors.groupingBy(
								Dish::getType, 
								Collectors.mapping(dish -> {
									if(dish.getCalories()<=400) return CaloricLevel.DIET;
									else if(dish.getCalories()<=700) return CaloricLevel.NORMAL;
									else return CaloricLevel.FAT;}, 
										Collectors.toSet())));
		System.out.println("结果："+caloricLevelByType);
		Map<Dish.Type, Set<CaloricLevel>> caloricLevelByType2 = 
				menu.stream().collect(
						Collectors.groupingBy(
								Dish::getType, 
								Collectors.mapping(dish -> {
									if(dish.getCalories()<=400) return CaloricLevel.DIET;
									else if(dish.getCalories()<=700) return CaloricLevel.NORMAL;
									else return CaloricLevel.FAT;}, 
										Collectors.toCollection(HashSet::new))));
		System.out.println("结果2："+caloricLevelByType2);
		
		System.out.println();
		
		System.out.println("分区");
		System.out.println("按照素食和非素食区分：");
		Map<Boolean, List<Dish>> partitionedMenu = 
				menu.stream().collect(
						Collectors.partitioningBy(Dish::isVegetarian));
		System.out.println("结果："+partitionedMenu);
		
		System.out.println("按照素食和非素食区分，并按类型分组：");
		Map<Boolean, Map<Dish.Type, List<Dish>>> vegetarianDishByType = 
				menu.stream().collect(
						Collectors.partitioningBy(Dish::isVegetarian,
								Collectors.groupingBy(Dish::getType)));
		System.out.println("结果："+vegetarianDishByType);
		
		System.out.println("找到素食和非素食中热量最高的菜：");
		Map<Boolean, Dish> mostPartitionedCaloriesByType = 
				menu.stream().collect(
					Collectors.partitioningBy(Dish::isVegetarian, 
							Collectors.collectingAndThen(
									Collectors.maxBy(
											Comparator.comparing(Dish::getCalories)), 
									Optional::get)));
		System.out.println("结果："+mostPartitionedCaloriesByType);
		
		System.out.println();
		
		System.out.println("将数值按质数和非质数分区：");
		Map<Boolean, List<Integer>> primes = partitionPrimes(10);
		System.out.println("结果："+primes);
	}
	
	/**
	 * 将2-n之间(不包括n)的质数和非质数分区
	 * @param n
	 * @return
	 */
	public static Map<Boolean, List<Integer>> partitionPrimes(int n){
		return IntStream.range(2, n).boxed()
				.collect(
						Collectors.partitioningBy(
								i -> isPrime2(i)));
	}
	
	/**
	 * 判断candidate是否为质数
	 * @param candidate
	 * @return
	 */
	public static boolean isPrime(int candidate) {
		return IntStream.range(2, candidate)
				.noneMatch(i -> candidate % i == 0);
	}
	
	/**
	 * 判断candidate是否为质数
	 * @param candidate
	 * @return
	 */
	public static boolean isPrime2(int candidate) {
		int candidateRoot = (int)Math.sqrt((double)candidate);
		return IntStream.rangeClosed(2, candidateRoot)
				.noneMatch(i -> candidate % i == 0);
	}
	
	enum CaloricLevel {DIET, NORMAL, FAT}

}
